Initial commit
[RGBW Controller] / RGBW_Controller / pwm.c
1 /*
2  * Copyright (C) 2016 Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18
19 /* Set the following three defines to your needs */
20
21 #ifndef SDK_PWM_PERIOD_COMPAT_MODE
22   #define SDK_PWM_PERIOD_COMPAT_MODE 0
23 #endif
24 #ifndef PWM_MAX_CHANNELS
25   #define PWM_MAX_CHANNELS 8
26 #endif
27 #define PWM_DEBUG 0
28 #define PWM_USE_NMI 0
29
30 /* no user servicable parts beyond this point */
31
32 #define PWM_MAX_TICKS 0x7fffff
33 #if SDK_PWM_PERIOD_COMPAT_MODE
34 #define PWM_PERIOD_TO_TICKS(x) (x * 0.2)
35 #define PWM_DUTY_TO_TICKS(x) (x * 5)
36 #define PWM_MAX_DUTY (PWM_MAX_TICKS * 0.2)
37 #define PWM_MAX_PERIOD (PWM_MAX_TICKS * 5)
38 #else
39 #define PWM_PERIOD_TO_TICKS(x) (x)
40 #define PWM_DUTY_TO_TICKS(x) (x)
41 #define PWM_MAX_DUTY PWM_MAX_TICKS
42 #define PWM_MAX_PERIOD PWM_MAX_TICKS
43 #endif
44
45 #include <c_types.h>
46 #include <pwm.h>
47 #include <eagle_soc.h>
48 #include <ets_sys.h>
49
50 // from SDK hw_timer.c
51 #define TIMER1_DIVIDE_BY_16             0x0004
52 #define TIMER1_ENABLE_TIMER             0x0080
53
54 struct pwm_phase {
55         uint32_t ticks;    ///< delay until next phase, in 200ns units
56         uint16_t on_mask;  ///< GPIO mask to switch on
57         uint16_t off_mask; ///< GPIO mask to switch off
58 };
59
60 /* Three sets of PWM phases, the active one, the one used
61  * starting with the next cycle, and the one updated
62  * by pwm_start. After the update pwm_next_set
63  * is set to the last updated set. pwm_current_set is set to
64  * pwm_next_set from the interrupt routine during the first
65  * pwm phase
66  */
67 typedef struct pwm_phase (pwm_phase_array)[PWM_MAX_CHANNELS + 2];
68 static pwm_phase_array pwm_phases[3];
69 static struct {
70         struct pwm_phase* next_set;
71         struct pwm_phase* current_set;
72         uint8_t current_phase;
73 } pwm_state;
74
75 static uint32_t pwm_period;
76 static uint32_t pwm_period_ticks;
77 static uint32_t pwm_duty[PWM_MAX_CHANNELS];
78 static uint16_t gpio_mask[PWM_MAX_CHANNELS];
79 static uint8_t pwm_channels;
80
81 // 3-tuples of MUX_REGISTER, MUX_VALUE and GPIO number
82 typedef uint32_t (pin_info_type)[3];
83
84 struct gpio_regs {
85         uint32_t out;         /* 0x60000300 */
86         uint32_t out_w1ts;    /* 0x60000304 */
87         uint32_t out_w1tc;    /* 0x60000308 */
88         uint32_t enable;      /* 0x6000030C */
89         uint32_t enable_w1ts; /* 0x60000310 */
90         uint32_t enable_w1tc; /* 0x60000314 */
91         uint32_t in;          /* 0x60000318 */
92         uint32_t status;      /* 0x6000031C */
93         uint32_t status_w1ts; /* 0x60000320 */
94         uint32_t status_w1tc; /* 0x60000324 */
95 };
96 static struct gpio_regs* gpio = (struct gpio_regs*)(0x60000300);
97
98 struct timer_regs {
99         uint32_t frc1_load;   /* 0x60000600 */
100         uint32_t frc1_count;  /* 0x60000604 */
101         uint32_t frc1_ctrl;   /* 0x60000608 */
102         uint32_t frc1_int;    /* 0x6000060C */
103         uint8_t  pad[16];
104         uint32_t frc2_load;   /* 0x60000620 */
105         uint32_t frc2_count;  /* 0x60000624 */
106         uint32_t frc2_ctrl;   /* 0x60000628 */
107         uint32_t frc2_int;    /* 0x6000062C */
108         uint32_t frc2_alarm;  /* 0x60000630 */
109 };
110 static struct timer_regs* timer = (struct timer_regs*)(0x60000600);
111
112 static void ICACHE_RAM_ATTR
113 pwm_intr_handler(void)
114 {
115         if ((pwm_state.current_set[pwm_state.current_phase].off_mask == 0) &&
116             (pwm_state.current_set[pwm_state.current_phase].on_mask == 0)) {
117                 pwm_state.current_set = pwm_state.next_set;
118                 pwm_state.current_phase = 0;
119         }
120
121         do {
122                 // force write to GPIO registers on each loop
123                 asm volatile ("" : : : "memory");
124
125                 gpio->out_w1ts = (uint32_t)(pwm_state.current_set[pwm_state.current_phase].on_mask);
126                 gpio->out_w1tc = (uint32_t)(pwm_state.current_set[pwm_state.current_phase].off_mask);
127
128                 uint32_t ticks = pwm_state.current_set[pwm_state.current_phase].ticks;
129
130                 pwm_state.current_phase++;
131
132                 if (ticks) {
133                         if (ticks >= 16) {
134                                 // constant interrupt overhead
135                                 ticks -= 9;
136                                 timer->frc1_int &= ~FRC1_INT_CLR_MASK;
137                                 WRITE_PERI_REG(&timer->frc1_load, ticks);
138                                 return;
139                         }
140
141                         ticks *= 4;
142                         do {
143                                 ticks -= 1;
144                                 // stop compiler from optimizing delay loop to noop
145                                 asm volatile ("" : : : "memory");
146                         } while (ticks > 0);
147                 }
148
149         } while (1);
150 }
151
152 /**
153  * period: initial period (base unit 1us OR 200ns)
154  * duty: array of initial duty values, may be NULL, may be freed after pwm_init
155  * pwm_channel_num: number of channels to use
156  * pin_info_list: array of pin_info
157  */
158 void ICACHE_FLASH_ATTR
159 pwm_init(uint32_t period, uint32_t *duty, uint32_t pwm_channel_num,
160               uint32_t (*pin_info_list)[3])
161 {
162         int i, j, n;
163
164         pwm_channels = pwm_channel_num;
165         if (pwm_channels > PWM_MAX_CHANNELS)
166                 pwm_channels = PWM_MAX_CHANNELS;
167
168         for (i = 0; i < 3; i++) {
169                 for (j = 0; j < (PWM_MAX_CHANNELS + 2); j++) {
170                         pwm_phases[i][j].ticks = 0;
171                         pwm_phases[i][j].on_mask = 0;
172                         pwm_phases[i][j].off_mask = 0;
173                 }
174         }
175         pwm_state.current_set = pwm_state.next_set = 0;
176         pwm_state.current_phase = 0;
177
178         uint32_t all = 0;
179         // PIN info: MUX-Register, Mux-Setting, PIN-Nr
180         for (n = 0; n < pwm_channels; n++) {
181                 pin_info_type* pin_info = &pin_info_list[n];
182                 PIN_FUNC_SELECT((*pin_info)[0], (*pin_info)[1]);
183                 gpio_mask[n] = 1 << (*pin_info)[2];
184                 all |= 1 << (*pin_info)[2];
185                 if (duty)
186                         pwm_set_duty(duty[n], n);
187         }
188         GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, all);
189         GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, all);
190
191         pwm_set_period(period);
192
193 #if PWM_USE_NMI
194         ETS_FRC_TIMER1_NMI_INTR_ATTACH(pwm_intr_handler);
195 #else
196         ETS_FRC_TIMER1_INTR_ATTACH(pwm_intr_handler, NULL);
197 #endif
198         TM1_EDGE_INT_ENABLE();
199
200         timer->frc1_int &= ~FRC1_INT_CLR_MASK;
201         timer->frc1_ctrl = 0;
202
203         pwm_start();
204 }
205
206 __attribute__ ((noinline))
207 static uint8_t ICACHE_FLASH_ATTR
208 _pwm_phases_prep(struct pwm_phase* pwm)
209 {
210         uint8_t n, phases;
211
212         uint16_t off_mask = 0;
213         for (n = 0; n < pwm_channels + 2; n++) {
214                 pwm[n].ticks = 0;
215                 pwm[n].on_mask = 0;
216                 pwm[n].off_mask = 0;
217         }
218         phases = 1;
219         for (n = 0; n < pwm_channels; n++) {
220                 uint32_t ticks = PWM_DUTY_TO_TICKS(pwm_duty[n]);
221                 if (ticks == 0) {
222                         pwm[0].off_mask |= gpio_mask[n];
223                 } else if (ticks >= pwm_period_ticks) {
224                         pwm[0].on_mask |= gpio_mask[n];
225                 } else {
226                         if (ticks < (pwm_period_ticks/2)) {
227                                 pwm[phases].ticks = ticks;
228                                 pwm[0].on_mask |= gpio_mask[n];
229                                 pwm[phases].off_mask = gpio_mask[n];
230                         } else {
231                                 pwm[phases].ticks = pwm_period_ticks - ticks;
232                                 pwm[phases].on_mask = gpio_mask[n];
233                                 pwm[0].off_mask |= gpio_mask[n];
234                         }
235                         phases++;
236                 }
237         }
238         pwm[phases].ticks = pwm_period_ticks;
239
240         // bubble sort, lowest to hightest duty
241         n = 2;
242         while (n < phases) {
243                 if (pwm[n].ticks < pwm[n - 1].ticks) {
244                         struct pwm_phase t = pwm[n];
245                         pwm[n] = pwm[n - 1];
246                         pwm[n - 1] = t;
247                         if (n > 2)
248                                 n--;
249                 } else {
250                         n++;
251                 }
252         }
253
254 #if PWM_DEBUG
255         int t = 0;
256         for (t = 0; t <= phases; t++) {
257                 ets_printf("%d @%d:   %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
258         }
259 #endif
260
261         // shift left to align right edge;
262         uint8_t l = 0, r = 1;
263         while (r <= phases) {
264                 uint32_t diff = pwm[r].ticks - pwm[l].ticks;
265                 if (diff && (diff <= 16)) {
266                         uint16_t mask = pwm[r].on_mask | pwm[r].off_mask;
267                         pwm[l].off_mask ^= pwm[r].off_mask;
268                         pwm[l].on_mask ^= pwm[r].on_mask;
269                         pwm[0].off_mask ^= pwm[r].on_mask;
270                         pwm[0].on_mask ^= pwm[r].off_mask;
271                         pwm[r].ticks = pwm_period_ticks - diff;
272                         pwm[r].on_mask ^= mask;
273                         pwm[r].off_mask ^= mask;
274                 } else {
275                         l = r;
276                 }
277                 r++;
278         }
279
280 #if PWM_DEBUG
281         for (t = 0; t <= phases; t++) {
282                 ets_printf("%d @%d:   %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
283         }
284 #endif
285
286         // sort again
287         n = 2;
288         while (n <= phases) {
289                 if (pwm[n].ticks < pwm[n - 1].ticks) {
290                         struct pwm_phase t = pwm[n];
291                         pwm[n] = pwm[n - 1];
292                         pwm[n - 1] = t;
293                         if (n > 2)
294                                 n--;
295                 } else {
296                         n++;
297                 }
298         }
299
300         // merge same duty
301         l = 0, r = 1;
302         while (r <= phases) {
303                 if (pwm[r].ticks == pwm[l].ticks) {
304                         pwm[l].off_mask |= pwm[r].off_mask;
305                         pwm[l].on_mask |= pwm[r].on_mask;
306                         pwm[r].on_mask = 0;
307                         pwm[r].off_mask = 0;
308                 } else {
309                         l++;
310                         if (l != r) {
311                                 struct pwm_phase t = pwm[l];
312                                 pwm[l] = pwm[r];
313                                 pwm[r] = t;
314                         }
315                 }
316                 r++;
317         }
318         phases = l;
319
320 #if PWM_DEBUG
321         for (t = 0; t <= phases; t++) {
322                 ets_printf("%d @%d:   %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
323         }
324 #endif
325
326         // transform absolute end time to phase durations
327         for (n = 0; n < phases; n++) {
328                 pwm[n].ticks =
329                         pwm[n + 1].ticks - pwm[n].ticks;
330                 // subtract common overhead
331                 pwm[n].ticks--;
332         }
333         pwm[phases].ticks = 0;
334
335         // do a cyclic shift if last phase is short
336         if (pwm[phases - 1].ticks < 16) {
337                 for (n = 0; n < phases - 1; n++) {
338                         struct pwm_phase t = pwm[n];
339                         pwm[n] = pwm[n + 1];
340                         pwm[n + 1] = t;
341                 }
342         }
343
344 #if PWM_DEBUG
345         for (t = 0; t <= phases; t++) {
346                 ets_printf("%d +%d:   %04x %04x\n", t, pwm[t].ticks, pwm[t].on_mask, pwm[t].off_mask);
347         }
348         ets_printf("\n");
349 #endif
350
351         return phases;
352 }
353
354 void ICACHE_FLASH_ATTR
355 pwm_start(void)
356 {
357         pwm_phase_array* pwm = &pwm_phases[0];
358
359         if ((*pwm == pwm_state.next_set) ||
360             (*pwm == pwm_state.current_set))
361                 pwm++;
362         if ((*pwm == pwm_state.next_set) ||
363             (*pwm == pwm_state.current_set))
364                 pwm++;
365
366         uint8_t phases = _pwm_phases_prep(*pwm);
367
368         // all with 0% / 100% duty - stop timer
369         if (phases == 1) {
370                 if (pwm_state.next_set) {
371 #if PWM_DEBUG
372                         ets_printf("PWM stop\n");
373 #endif
374                         timer->frc1_ctrl = 0;
375                         ETS_FRC1_INTR_DISABLE();
376                 }
377                 pwm_state.next_set = NULL;
378
379                 GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (*pwm)[0].on_mask);
380                 GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, (*pwm)[0].off_mask);
381
382                 return;
383         }
384
385         // start if not running
386         if (!pwm_state.next_set) {
387 #if PWM_DEBUG
388                 ets_printf("PWM start\n");
389 #endif
390                 pwm_state.current_set = pwm_state.next_set = *pwm;
391                 pwm_state.current_phase = phases - 1;
392                 ETS_FRC1_INTR_ENABLE();
393                 RTC_REG_WRITE(FRC1_LOAD_ADDRESS, 0);
394                 timer->frc1_ctrl = TIMER1_DIVIDE_BY_16 | TIMER1_ENABLE_TIMER;
395                 return;
396         }
397
398         pwm_state.next_set = *pwm;
399 }
400
401 void ICACHE_FLASH_ATTR
402 pwm_set_duty(uint32_t duty, uint8_t channel)
403 {
404         if (channel > PWM_MAX_CHANNELS)
405                 return;
406
407         if (duty > PWM_MAX_DUTY)
408                 duty = PWM_MAX_DUTY;
409
410         pwm_duty[channel] = duty;
411 }
412
413 uint32_t ICACHE_FLASH_ATTR
414 pwm_get_duty(uint8_t channel)
415 {
416         if (channel > PWM_MAX_CHANNELS)
417                 return 0;
418         return pwm_duty[channel];
419 }
420
421 void ICACHE_FLASH_ATTR
422 pwm_set_period(uint32_t period)
423 {
424         pwm_period = period;
425
426         if (pwm_period > PWM_MAX_PERIOD)
427                 pwm_period = PWM_MAX_PERIOD;
428
429         pwm_period_ticks = PWM_PERIOD_TO_TICKS(period);
430 }
431
432 uint32_t ICACHE_FLASH_ATTR
433 pwm_get_period(void)
434 {
435         return pwm_period;
436 }
437
438 uint32_t ICACHE_FLASH_ATTR
439 get_pwm_version(void)
440 {
441         return 1;
442 }
443
444 void ICACHE_FLASH_ATTR
445 set_pwm_debug_en(uint8_t print_en)
446 {
447         (void) print_en;
448 }
449
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717